package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/grpc/imp/enums"
	"bgs/service/model"
	"bgs/util"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// UpsertRequestParam 创建/修改本地分账请求参数
type UpsertRequestParam struct {
	OrderPtr              *sqlc.Order
	PaymentAmount         int64 // 订单实际支付的金额
	PlatformCommission    int64
	DistributorCommission int64
	BonusAmount           int64
}

// UpsertRequest 创建本地分账请求 (主动)
func (s *LedgerServiceImp) UpsertRequest(ctx context.Context, q *sqlc.Queries, p UpsertRequestParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	brandID := p.OrderPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	// sgID := cBrand.SgID

	// unSplitAmount, err := s.queryProfitOrderUnSplitAmount(ctx, q, sgID, p.OrderPtr.SerialNumber)
	// if err != nil {
	// 	return err
	// }

	// 查询订单剩余待分账金额,没有分过账所以无法查询可分账金额
	// 因此还是自己来算扣除微信的可分账金额=交易金额-微信手续费(按1%费率)
	lockForWxCommission := int64(util.Fix0(0.01 * float64(p.PaymentAmount)))
	unSplitAmount := p.PaymentAmount - lockForWxCommission
	submchProfitAmount := int64(unSplitAmount) - p.PlatformCommission - p.DistributorCommission - p.BonusAmount

	var profitReceiverPtrs []*model.ProfitReceiver

	profitReceiverPtrsAtOrderFinished, err := s.createRequestAtOrderFinished(ctx, q, p.OrderPtr, cBrand.Ext.WxpayMchIDAsSub, submchProfitAmount)
	if err != nil {
		return err
	}
	profitReceiverPtrs = append(profitReceiverPtrs, profitReceiverPtrsAtOrderFinished...)
	slog.Infof("创建订单分账请求(at_order_finished)成功")

	profitReceiverPtrsAtAfterSaleDeadline, err := s.createRequestAtAfterSaleDeadline(ctx, q, p.OrderPtr, p.PlatformCommission, p.DistributorCommission, p.BonusAmount)
	if err != nil {
		return err
	}
	profitReceiverPtrs = append(profitReceiverPtrs, profitReceiverPtrsAtAfterSaleDeadline...)
	slog.Infof("创建订单分账请求(at_after_sale_deadline)成功")

	// nsqp.ProducerOfBgsProxy
	if len(profitReceiverPtrs) > 0 {
		for _, profitReceiverPtr := range profitReceiverPtrs {
			// 发送条件分账者消息
			err = s.ledgerRunner.PublishAddProfitReceiver(profitReceiverPtr)
			if err != nil {
				slog.Infof("UpsertRequest 发送分账者消息失败err: %s", err.Error())
				// 此处不往上抛错误,直接打印
				err = nil
			} else {
				slog.Infof("UpsertRequest 发送分账者消息(%+v) ok", profitReceiverPtr)
			}
		}
	}

	slog.Infof("UpsertRequest 创建订单分账请求(全场景) ok")
	return
}

func (s *LedgerServiceImp) createRequestAtOrderFinished(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order, submchID string, submchProfitAmount int64) (profitReceiverPtrs []*model.ProfitReceiver, err error) {
	brandID := orderPtr.BrandID
	serialNumber := s.generateLedgerSerialNumber("LQ", orderPtr.SerialNumber)
	s.slog.Info("createRequestAtOrderFinished gen serialNumber:%s", serialNumber)
	ledgerReq, err := q.UpsertLedgerRequest(ctx, sqlc.UpsertLedgerRequestParams{
		BrandID:            brandID,
		OrderID:            orderPtr.ID,
		OrderSerialNumber:  orderPtr.SerialNumber,
		SerialNumber:       serialNumber,
		SubmchProfitAmount: submchProfitAmount,
		ProfitTrigger:      string(sqlc.EmProfitTriggerAtOrderStatusToFinish),
		LastFlag:           false,
	})
	if err != nil {
		return
	}
	s.slog.Info("createRequestAtOrderFinished UpsertLedgerRequest(%s) ok", serialNumber)
	addProfitReceiverTimestamp := time.Now().UnixNano()

	wxpayApplyment, err := q.GetWxpayApplymentByBrandID(ctx, brandID)
	if err != nil {
		return
	}
	wxpayApplymentFormPtr := &model.WxpayApplymentForm{}
	err = json.Unmarshal(wxpayApplyment.ApplyFormData, wxpayApplymentFormPtr)
	if err != nil {
		return
	}
	var subMerchantProfitReceiverName string
	switch enums.EnumWxpayApplymentOrganizationType(wxpayApplymentFormPtr.OrganizationType) {
	case enums.WxpayApplymentOrganizationType_Micro:
		subMerchantProfitReceiverName = fmt.Sprintf("商户_%s", wxpayApplymentFormPtr.IDCardInfoPtr.IDCardName)
	case enums.WxpayApplymentOrganizationType_Individual:
		subMerchantProfitReceiverName = wxpayApplymentFormPtr.BizLicenseInfoPtr.MerchantName
	case enums.WxpayApplymentOrganizationType_company:
		subMerchantProfitReceiverName = wxpayApplymentFormPtr.BizLicenseInfoPtr.MerchantName
	}

	subMerchantProfitReceiver := &model.ProfitReceiver{
		Type:         string(ProfitReceiverType_MerchantID),
		Account:      submchID,
		Name:         subMerchantProfitReceiverName,
		RelationType: string(ProfitReceiverRelationType_OTHERS),
		Timestamp:    addProfitReceiverTimestamp,
	}

	receiverBytes, marshalErr := json.Marshal(subMerchantProfitReceiver)
	if marshalErr != nil {
		err = marshalErr
		return
	}
	// 目前在订单完成就把自商户应得部分走分账解冻
	_, err = q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
		Category:                  sqlc.NSFromString(string(sqlc.EmLedgerCategorySubmchProfit)),
		LedgerRequestID:           sqlc.NInt64FromInt64(ledgerReq.ID),
		LedgerRequestSerialNumber: sqlc.NSFromString(ledgerReq.SerialNumber),
		BrandID:                   ledgerReq.BrandID,
		OrderID:                   ledgerReq.OrderID,
		OrderSerialNumber:         ledgerReq.OrderSerialNumber,
		Receiver:                  receiverBytes,
		Amount:                    submchProfitAmount,
		Remark:                    sqlc.NSFromString("品牌订单收入"),
	})
	if err != nil {
		return
	}
	profitReceiverPtrs = append(profitReceiverPtrs, subMerchantProfitReceiver)
	return
}

func (s *LedgerServiceImp) createRequestAtAfterSaleDeadline(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order, platformCommission, distributorCommission, bonusAmount int64) (profitReceiverPtrs []*model.ProfitReceiver, err error) {
	ledgerReq, err := q.UpsertLedgerRequest(ctx, sqlc.UpsertLedgerRequestParams{
		BrandID:               orderPtr.BrandID,
		OrderID:               orderPtr.ID,
		OrderSerialNumber:     orderPtr.SerialNumber,
		SerialNumber:          s.generateLedgerSerialNumber("LQ", orderPtr.SerialNumber),
		PlatformCommission:    platformCommission,
		DistributorCommission: distributorCommission,
		BonusAmount:           bonusAmount,
		ProfitTrigger:         string(sqlc.EmProfitTriggerAtAfterSaleDeadline),
		LastFlag:              true,
	})
	if err != nil {
		return
	}
	addProfitReceiverTimestamp := time.Now().UnixNano()

	if platformCommission > 0 {
		platformReceiverPtr := &model.ProfitReceiver{
			Type:         string(ProfitReceiverType_MerchantID),
			Account:      s.vars.MchIDAsProfit,
			Name:         s.vars.MchNameAsProfit,
			RelationType: string(ProfitReceiverRelationType_SERVICE_PROVIDER),
			Timestamp:    addProfitReceiverTimestamp,
		}
		receiverBytes, marshalErr := json.Marshal(platformReceiverPtr)
		if marshalErr != nil {
			err = marshalErr
			return
		}

		_, err = q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                  sqlc.NSFromString(string(sqlc.EmLedgerCategoryPlatformCommission)),
			LedgerRequestID:           sqlc.NInt64FromInt64(ledgerReq.ID),
			LedgerRequestSerialNumber: sqlc.NSFromString(ledgerReq.SerialNumber),
			BrandID:                   ledgerReq.BrandID,
			OrderID:                   ledgerReq.OrderID,
			OrderSerialNumber:         ledgerReq.OrderSerialNumber,
			Receiver:                  receiverBytes,
			Amount:                    platformCommission,
			Remark:                    sqlc.NSFromString("平台佣金"),
		})
		if err != nil {
			return
		}
		profitReceiverPtrs = append(profitReceiverPtrs, platformReceiverPtr)
	}
	if distributorCommission > 0 {
		// 目前分销商是作为个人,无需银行账号
		distributorUser, err2 := q.GetUser(ctx, orderPtr.DistributorUserID.Int64)
		if err2 != nil {
			err = err2
			return
		}
		distributorReceiverPtr := &model.ProfitReceiver{
			Type:         string(ProfitReceiverType_PERSONAL_OPENID),
			Account:      distributorUser.OpenID,
			Name:         distributorUser.RealName.String,
			RelationType: string(ProfitReceiverRelationType_DISTRIBUTOR),
			Timestamp:    addProfitReceiverTimestamp,
		}
		receiverBytes, marshalErr := json.Marshal(distributorReceiverPtr)
		if marshalErr != nil {
			err = marshalErr
			return
		}
		_, err = q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                  sqlc.NSFromString(string(sqlc.EmLedgerCategoryOrderDistributorCommission)),
			LedgerRequestID:           sqlc.NInt64FromInt64(ledgerReq.ID),
			LedgerRequestSerialNumber: sqlc.NSFromString(ledgerReq.SerialNumber),
			BrandID:                   ledgerReq.BrandID,
			OrderID:                   ledgerReq.OrderID,
			OrderSerialNumber:         ledgerReq.OrderSerialNumber,
			Receiver:                  receiverBytes,
			Amount:                    distributorCommission,
			Remark:                    sqlc.NSFromString("品牌分销订单佣金"),
		})
		if err != nil {
			return
		}
		profitReceiverPtrs = append(profitReceiverPtrs, distributorReceiverPtr)
	}
	if bonusAmount > 0 {
		spokesmanUser, err2 := q.GetUser(ctx, orderPtr.SpokesmanUserID.Int64)
		if err2 != nil {
			err = err2
			return
		}
		spokesmanReceiverPtr := &model.ProfitReceiver{
			Type:         string(ProfitReceiverType_PERSONAL_OPENID),
			Account:      spokesmanUser.OpenID,
			Name:         spokesmanUser.RealName.String,
			RelationType: string(ProfitReceiverRelationType_OTHERS),
			Timestamp:    addProfitReceiverTimestamp,
		}
		receiverBytes, marshalErr := json.Marshal(spokesmanReceiverPtr)
		if marshalErr != nil {
			err = marshalErr
			return
		}
		_, err = q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                  sqlc.NSFromString(string(sqlc.EmLedgerCategoryOrderBonusProfit)),
			LedgerRequestID:           sqlc.NInt64FromInt64(ledgerReq.ID),
			LedgerRequestSerialNumber: sqlc.NSFromString(ledgerReq.SerialNumber),
			BrandID:                   ledgerReq.BrandID,
			OrderID:                   ledgerReq.OrderID,
			OrderSerialNumber:         ledgerReq.OrderSerialNumber,
			Receiver:                  receiverBytes,
			Amount:                    bonusAmount,
			Remark:                    sqlc.NSFromString("订单奖励金"),
		})
		if err != nil {
			return
		}
		profitReceiverPtrs = append(profitReceiverPtrs, spokesmanReceiverPtr)
	}
	return
}

// RequestToProxy 订单完成时request ledger to remote proxy (主动)
func (s *LedgerServiceImp) RequestToProxy(ctx context.Context, q *sqlc.Queries, ledgerRequestPtr *sqlc.LedgerRequest) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	brandID := ledgerRequestPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID

	// 根据ledger_details查找分账人及各自的分账资金
	ledgerDetailPtrs, err := s.ListLedgerDetailsByRequestID(ctx, q, ledgerRequestPtr.ID)
	if err != nil {
		return
	}
	slog.Infof("RequestToProxy-ListLedgerDetailsByRequestID ok")

	// 是否订单的所有分账请求都已经完结(finished+closed)
	isFinish := ledgerRequestPtr.LastFlag
	receivers := s.ConvLedgerDetailPtrsToProfitReceiverPtrs(ledgerDetailPtrs)
	// receivers = append(receivers, {Type: "MERCHANT_ID", ReceiverAccount: s.vars.MchIDAsProfit, Amount: ledgerRequestPtr.Amount, Description: "订单佣金"})
	wxLedgerOrderNo, err := s.submitProfitOrder(ctx, q, sgID, receivers, ledgerRequestPtr, isFinish)
	if err != nil {
		return
	}

	err = q.UpdateLedgerRequestWxLedgerOrderNo(ctx, sqlc.UpdateLedgerRequestWxLedgerOrderNoParams{
		ID:              ledgerRequestPtr.ID,
		Submitted:       sqlc.NTimeFromTime(time.Now()),
		WxLedgerOrderNo: sqlc.NSFromString(wxLedgerOrderNo),
	})
	if err != nil {
		return
	}
	slog.Infof("订单完成时提交(场景:%s)订单分账请求成功", ledgerRequestPtr.ProfitTrigger)
	return
}

// AfterRequestSubmitSuccessParam ledger request success param
type AfterRequestSubmitSuccessParam struct {
	WxLedgerOrderNo    string
	LedgerSerialNumber string
	OrderSerialNumber  string
	Occurred           time.Time
}

// DoAfterRequestSubmitSuccess ledger request success do
func (s *LedgerServiceImp) DoAfterRequestSubmitSuccess(ctx context.Context, q *sqlc.Queries, p AfterRequestSubmitSuccessParam) (ledgerRequestPtr *sqlc.LedgerRequest, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	ledgerRequest, err := q.FinishLedgerRequestBySerialNumber(ctx, sqlc.FinishLedgerRequestBySerialNumberParams{
		SerialNumber: p.LedgerSerialNumber,
		Finished:     sqlc.NTimeFromTime(p.Occurred),
	})
	if err != nil {
		return
	}
	slog.Infof("1、更新分账请求(%s)完成时间 ok", p.LedgerSerialNumber)

	ledgerRequestPtr = &ledgerRequest
	return
}

// MarkLedgerRequestAccounted 标记订单奖励金分账已记账
func (s *LedgerServiceImp) MarkLedgerRequestAccounted(ctx context.Context, q *sqlc.Queries, ledgerSerialNumber string) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	err = q.MarkLedgerRequestAccountedBySerialNumber(ctx, ledgerSerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1 标记订单奖励金分账已记账 ok")
	return
}

// AddProfitReceiver wxpay添加分账者
func (s *LedgerServiceImp) AddProfitReceiver(ctx context.Context, receiver *model.ProfitReceiver) (err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	// 此处分账是将佣金划入平台资金账户
	req := &wxpay.ProfitReceiverAddRequest{
		MchId:        s.vars.MchIDAsService,
		AppId:        s.wechatVars.MinaAppID,
		Type:         receiver.Type,
		Account:      receiver.Account,
		Name:         receiver.Name,
		RelationType: receiver.RelationType,
	}

	s.slog.Infof("AddProfitReceiver req:%+v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	_, err = s.proxy.WxpayCl.ProfitReceiverAdd(ctxWithTimout, req)
	if err != nil {
		return
	}
	return
}

// QueryProfitOrderUnSplitAmount wxpay查询订单剩余待分金额
func (s *LedgerServiceImp) QueryProfitOrderUnSplitAmount(ctx context.Context, q *sqlc.Queries, sgID int64, outTradeNo string) (unSplitAmount uint64, err error) {
	req := &wxpay.UnSplitAmountQueryRequest{
		BrandId:    strconv.FormatInt(sgID, 10), // 这里的wxpay里的brandId其实是原来的sg_id
		OutTradeNo: outTradeNo,
	}
	s.slog.Infof("wxpay.UnSplitAmountQuery req:%+v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	res, err := s.proxy.WxpayCl.UnSplitAmountQuery(ctxWithTimout, req)
	if err != nil {
		return
	}
	unSplitAmount = uint64(res.UnsplitAmount)
	return
}

// submitProfitOrder wxpay提交分账请求
func (s *LedgerServiceImp) submitProfitOrder(ctx context.Context, q *sqlc.Queries, sgID int64, receivers []*wxpay.ProfitReceiver, ledgerRequestPtr *sqlc.LedgerRequest, isFinish bool) (wxLedgerOrderNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	req := &wxpay.ProfitOrderRequest{
		BrandId:    strconv.FormatInt(sgID, 10), // 这里的wxpay里的brandId其实是原来的sg_id
		OutTradeNo: ledgerRequestPtr.OrderSerialNumber,
		OutOrderNo: ledgerRequestPtr.SerialNumber,
		Receivers:  receivers,
		Finish:     isFinish,
	}

	s.slog.Infof("wxpay.submitProfitOrder req:%+v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	res, err := s.proxy.WxpayCl.ProfitOrderSubmit(ctxWithTimout, req)
	if err != nil {
		return
	}
	wxLedgerOrderNo = res.WxOrderId
	return
}

/*
// profitShareFinish wxpay完成分账请求 deprecete
func (s *LedgerServiceImp) profitShareFinish(ctx context.Context, ledgerRequestPtr *sqlc.LedgerRequest) (wxLedgerOrderNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	cSyncMappingBrand := s.cacher.FetchSyncMappingByLocalObjAndLocalPk(sqlc.GetSyncMappingByLocalObjAndLocalPkParams{
		LocalObj: constants.SyncLocalObjBrand,
		LocalPk:  strconv.FormatInt(ledgerRequestPtr.BrandID, 10),
	})
	if cSyncMappingBrand == nil {
		err = fmt.Errorf("not found sg_id of brandID:%d", ledgerRequestPtr.BrandID)
		return
	}
	sgID := cSyncMappingBrand.RemotePk

	// 此处分账是将佣金划入平台资金账户
	req := &wxpay.ProfitShareFinishRequest{
		BrandId:     sgID, // 这里的wxpay里的brandId其实是原来的sg_id
		OutTradeNo:  ledgerRequestPtr.OrderSerialNumber,
		OutOrderNo:  ledgerRequestPtr.OrderSerialNumber,
		Description: "分账完结",
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	res, err := s.proxy.WxpayCl.ProfitShareFinishSubmit(ctxWithTimout, req)
	if err != nil {
		return
	}
	wxLedgerOrderNo = res.WxOrderId
	return
}
*/
